package org.aergia.vinny.gui.view;

import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.print.PrinterException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.SortOrder;

import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.view.JasperViewer;

import org.aergia.vinny.core.ApiResult;
import org.aergia.vinny.database.pojo.Service;
import org.aergia.vinny.database.pojo.VinnyUser;
import org.aergia.vinny.gui.controller.VinnyController;
import org.aergia.vinny.gui.types.ColumnDefinition;
import org.aergia.vinny.gui.util.DialogUtil;
import org.aergia.vinny.gui.view.ServiceReportDialog.ReportGroup;
import org.aergia.vinny.gui.view.UserFrame.UserFramePanel;
import org.aergia.vinny.util.DateUtil;
import org.aergia.vinny.util.IconUtil;

public class ServicePanel extends DatabasePojoPanel {

	private static class ServiceTableModel extends DatabasePojoTableModel<Service> {

		private static List<ColumnDefinition> definitions;
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		static {
			ServiceTableModel.definitions = new ArrayList<ColumnDefinition>();
			ServiceTableModel.definitions.add(new ColumnDefinition("Active", Boolean.class));
			ServiceTableModel.definitions.add(new ColumnDefinition("Volunteer", String.class, 150));
			ServiceTableModel.definitions.add(new ColumnDefinition("Start", String.class, DatabasePojoTableModel.DATE_COLUMN_WIDTH, 0, SortOrder.DESCENDING));
			ServiceTableModel.definitions.add(new ColumnDefinition("Stop", String.class, DatabasePojoTableModel.DATE_COLUMN_WIDTH));
			ServiceTableModel.definitions.add(new ColumnDefinition("Time Frame", String.class, 100));
			ServiceTableModel.definitions.add(new ColumnDefinition("Duration", String.class, DatabasePojoTableModel.DURATION_COLUMN_WIDTH));
			ServiceTableModel.definitions.add(new ColumnDefinition("Approved", Boolean.class));
			ServiceTableModel.definitions.add(new ColumnDefinition("By", String.class, 150));
			ServiceTableModel.definitions.add(new ColumnDefinition("Service Type", String.class, 150));
			ServiceTableModel.definitions.add(new ColumnDefinition("Comment", String.class));
		}

		public ServiceTableModel(final VinnyController controller) {
			super(controller, ServiceTableModel.definitions);
		}

		@Override
		public Object getValueAt(final int rowIndex, final int columnIndex) {
			final Service service = this.records.get(rowIndex);
			switch (columnIndex) {
				case 0:
					return Boolean.valueOf(service.isActive());
				case 1:
					return service.getVolunteer().getName();
				case 2:
					return DateUtil.format(service.getStartTime());
				case 3:
					return DateUtil.format(service.getEndTime());
				case 4:
					return service.getTimeFrameAsPojo().name();
				case 5:
					return DateUtil.formatDuration(service.getDuration());
				case 6:
					return Boolean.valueOf(service.isApproved());
				case 7:
					return service.getApprovedBy() != null ? service.getApprovedBy().getName() : "";
				case 8:
					return service.getServiceType() != null ? service.getServiceType().getName() : "";
				case 9:
					return service.getComment() != null ? service.getComment() : "";
				default:
					return null;
			}
		}

		@Override
		protected void populateTable() {
			this.records.addAll(this.controller.getServices());
		}
	}

	private static Logger logger = Logger.getLogger("ServicePanel");

	private static final long serialVersionUID = 1L;

	/**
	 * @param controller
	 * @param user
	 */
	public ServicePanel(final VinnyController controller, final VinnyUser user, final UserFramePanel additionalPanel) {
		super(controller, user, new ServiceTableModel(controller), true, true, additionalPanel);
		additionalPanel.setActionListener(this);
	}

	@Override
	public void printTable() throws PrinterException {

		final ServiceReportDialog dialog = new ServiceReportDialog(this.controller);
		dialog.setVisible(true);

		if (dialog.getFileName() == null) {
			ServicePanel.logger.info("user canceled.");
			return;
		}

		dialog.getStartDate();
		dialog.getEndDate();

		final String sqlStatement = this.createSqlStatement(dialog);
		final Map<String, Object> parameters = new HashMap<String, Object>();
		parameters.put("START_DATE", dialog.getStartDate());
		parameters.put("END_DATE", dialog.getEndDate());
		parameters.put("LOCATION", "Resource Center");
		parameters.put("QUERY", sqlStatement);

		final String reportName = "/reports/service" + (dialog.isSummary() ? "-summary" : "") + ".jasper";
		ServicePanel.logger.info("Report Name: " + reportName);
		for (final String parameter : parameters.keySet()) {
			ServicePanel.logger.info(parameter + "=" + parameters.get(parameter));
		}

		try {
			final InputStream report = this.getClass().getResourceAsStream(reportName);
			ServicePanel.logger.info("jasper fill manager started.");

			ServicePanel.logger.info("connection = " + this.controller.getDatabaseConnection().toString());
			final JasperPrint jasperPrint = JasperFillManager.fillReport(report, parameters, this.controller.getDatabaseConnection());
			ServicePanel.logger.info("jasper fill manager finished.");

			final JasperViewer viewer = new JasperViewer(jasperPrint, false);
			viewer.setTitle("Service Hour Viewer");
			viewer.setVisible(true);
			viewer.setIconImage(IconUtil.getIconImage("/images/16/trackin_it.png"));

			// JasperExportManager.exportReportToPdfFile(jasperPrint,
			// dialog.getFileName());
			// ServicePanel.logger.info("jasper export to pdf file finished.");

		} catch (final Exception e) {
			ServicePanel.logger.log(Level.SEVERE, "Could not print " + reportName, e);
			throw new PrinterException(e.getLocalizedMessage());
		} catch (final Throwable t) {
			ServicePanel.logger.log(Level.SEVERE, "Could not print " + reportName, t);
			throw new PrinterException(t.getLocalizedMessage());
		}
	}

	@Override
	protected void addButtonActionPerformed(final ActionEvent event) {
		final ServiceEditor editor = new ServiceEditor(this.controller, this.user);
		final EditorDialog dialog = new EditorDialog(editor);

		if (dialog.isSaved()) {
			final ServiceTableModel serviceTableModel = (ServiceTableModel) this.tableModel;
			serviceTableModel.addNewRecord(editor.getService());
			this.refresh();
		}

	}

	@Override
	protected void approveAllButtonActionPerformed(final ActionEvent event) {
		final int[] selectedRows = this.getSelectedRows();
		if (selectedRows.length == 0) {
			return;
		}
		this.setCursor(new Cursor(Cursor.WAIT_CURSOR));
		final ServiceTableModel serviceTableModel = (ServiceTableModel) this.tableModel;
		for (final int selectedRow : selectedRows) {
			final Service service = serviceTableModel.getRecord(selectedRow);
			service.setApproved(true);
			service.setApprovedBy(this.user);
			final ApiResult<Service> result = this.controller.saveService(service, this.user);
			if (result.isError()) {
				DialogUtil.alert(this, result.getText(), "Error");
			}
		}
		this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
		this.refresh();
	}

	@Override
	protected void deleteButtonActionPerformed(final ActionEvent event) {
		final int selectedRow = this.getSelectedRow();
		if (selectedRow == -1) {
			return;
		}
		final ServiceTableModel serviceTableModel = (ServiceTableModel) this.tableModel;
		final Service service = serviceTableModel.getRecord(selectedRow);

		if (!DialogUtil.askQuestion(this, "Are you sure you want to disable service hours for " + service.getVolunteer().getName(), "Disable volunteer")) {
			return;
		}

		service.setActive(false);
		service.setApproved(false);
		service.setApprovedBy(null);
		service.setComment("Service hours disabled by " + this.user.getName() + ".");
		final ApiResult<Service> result = this.controller.saveService(service, this.user);
		if (!result.isError()) {
			serviceTableModel.setRecord(selectedRow, service);
			this.tableModel.fireTableRowsUpdated(selectedRow, selectedRow);
			this.refresh();
		} else {
			DialogUtil.alert(this, result.getText(), "Error");
		}

	}

	@Override
	protected void editButtonActionPerformed(final ActionEvent event) {
		final int selectedRow = this.getSelectedRow();
		if (selectedRow == -1) {
			return;
		}
		final ServiceTableModel serviceTableModel = (ServiceTableModel) this.tableModel;
		final Service service = serviceTableModel.getRecord(selectedRow);
		final ServiceEditor editor = new ServiceEditor(this.controller, this.user, service);
		final EditorDialog dialog = new EditorDialog(editor);

		if (dialog.isSaved()) {
			serviceTableModel.setRecord(selectedRow, service);
			this.tableModel.fireTableRowsUpdated(selectedRow, selectedRow);
			this.refresh();
		}
	}

	/**
	 * @param dialog
	 */
	private String createSqlStatement(final ServiceReportDialog dialog) {

		final ReportGroup reportGroup = dialog.getReportGroup();

		final String select = "select Service.volunteer_id, "
				+ (dialog.isSummary() ? "sum(Service.duration)" : "Service.duration")
				+ " as service_duration, Service.startTime service_startTime, Volunteer.firstName as volunteer_firstName, Volunteer.lastName as volunteer_lastName, VolunteerGroup.name as volunteergroup_name";

		final String from = "from Service inner join Volunteer on Service.volunteer_id = Volunteer.id left join VolunteerGroup on Volunteer.group_id = VolunteerGroup.id";

		String where = "where cast(Service.startTime as date) between cast('" + dialog.getStartDate() + "' as date) and cast('" + dialog.getEndDate()
				+ "' as date) and Service.active=true and Service.approved=true";

		switch (reportGroup) {
			case All:
				break;
			case Group:
				where += " and VolunteerGroup.name='" + dialog.getGroupName() + "'";
				break;
			case Individual:
				where += " and Service.volunteer_id=" + dialog.getVolunteer().getId();
		}

		String groupBy = "";
		if (dialog.isSummary()) {
			groupBy = "group by Service.volunteer_id";
		}
		final String orderBy = "order by VolunteerGroup.name, Volunteer.lastName, Volunteer.firstName, Service.startTime";
		final String sql = select + "\n" + from + "\n" + where + "\n" + groupBy + "\n" + orderBy + "\n";
		return sql;
	}
}
